package logic.baselogic;

import database.IDBObject;
import database.Record;
import logic.files.IDatabaseLoader;
import logic.files.IDatabaseSaver;
import logic.files.SimpleFileDatabaseLoader;
import logic.files.SimpleFileDatabaseSaver;
import logic.messagelogic.Command;
import logic.messagelogic.Message;
import logic.messagelogic.MessageConstants;

import java.util.ArrayList;

/**
 * Created by IntelliJ IDEA.
 * User: grigory.rozhkov
 * Date: 12.09.12
 * Time: 1:09
 * To change this template use File | Settings | File Templates.
 */
public class Database {
    public static final int MAX_NON_SAVED_TRANSACTIONS = 10;
    private ArrayList<IDBObject> baseList = new ArrayList<IDBObject>();
    //added objects since last sync
    private ArrayList<IDBObject> addedList = new ArrayList<IDBObject>(MAX_NON_SAVED_TRANSACTIONS);

    public void setBaseList(ArrayList<IDBObject> baseList) {
        this.baseList = baseList;
    }

    public ArrayList<IDBObject> getAddedList() {
        return addedList;
    }

    public ArrayList<IDBObject> getBaseList() {
        return baseList;
    }

    public static Database initBase() {
        IDatabaseLoader databaseLoader = new SimpleFileDatabaseLoader();
        return databaseLoader.loadBase();
    }

    public void saveBase() {
        IDatabaseSaver databaseSaver = new SimpleFileDatabaseSaver();
        databaseSaver.saveBase(this);
    }

    public boolean delete(String id) {
        IDBObject object = findDBObjectById(id);
        if (object == null) {
            return false;
        }
        object.delete();
        return true;
    }

    public boolean create(String id, String description) {
        IDBObject newObject = new Record();
        newObject.create(id, description);
        if (findDBObjectById(id) != null) {
            return false;
        }
        addedList.add(newObject);
        return true;
    }

    public boolean update(String id, String description) {
        IDBObject object = findDBObjectById(id);
        return object != null && object.update(description);
    }

    public IDBObject retrieve(String id) {
        return findDBObjectById(id);
    }

    public String applyMessage(Message message) {
        IDBObject subject = findDBObjectById(message.getId());
        if ((subject == null) && (!message.getCommand().getCommandName().equals(MessageConstants.CREATE)))
            return MessageConstants.FAILED;
        if (!Command.listOfSupportedCommands.containsKey(message.getCommand().getCommandName()))
            return MessageConstants.FAILED;
        for (String com : Command.listOfSupportedCommands.keySet()) {
            if (com.equals(message.getCommand().getCommandName())) {
                if (com.equals(MessageConstants.CREATE)) {
                    return create(message.getId(), message.getParameters()) ? "" : MessageConstants.FAILED;
                }
                if (com.equals(MessageConstants.UPDATE)) {
                    return update(message.getId(), message.getParameters()) ? "" : MessageConstants.FAILED;
                }
                if (com.equals(MessageConstants.DELETE)) {
                    return delete(message.getId()) ? "" : MessageConstants.FAILED;
                }
                if (com.equals(MessageConstants.RETRIEVE)) {
                    return subject == null ? MessageConstants.FAILED : subject.convertToString();
                }
                return MessageConstants.FAILED;
            }
        }
        return MessageConstants.FAILED;
    }

    //Since ArrayList takes O(1) to get element from the position
    //we will use binary search
    private IDBObject findDBObjectById(String id) {
        IDBObject foundObject = null;
        //let's check if we added it recently
        for (IDBObject currentObject : addedList) {
            if ((currentObject.getId().equals(id)) && (!currentObject.isDeleted())) {
                foundObject = currentObject;
            }
        }
        if (foundObject == null) {
            foundObject = findDBObjectByIdInSublist(id, 0, baseList.size() - 1);
        }
        return foundObject == null ? null : foundObject.isDeleted() ? null : foundObject;
    }

    private IDBObject findDBObjectByIdInSublist(String id, int a, int b) {
        if (baseList.isEmpty()) return null;
        if (a == b) {
            if ((id.equals(baseList.get(a).getId())) && (!baseList.get(a).isDeleted())) {
                return baseList.get(a);
            }
            return null;
        } else if (id.equals(baseList.get(a).getId())) {
            return baseList.get(a).isDeleted() ? null : baseList.get(a);
        } else if (id.equals(baseList.get(b).getId())) {
            return baseList.get(b).isDeleted() ? null : baseList.get(b);
        } else {
            int middle = (a + b) / 2;
            switch (id.compareTo(baseList.get(middle).getId())) {
                case 0: {
                    return baseList.get(middle).isDeleted() ? null : baseList.get(middle);
                }
                case 1: {
                    return findDBObjectByIdInSublist(id, middle, b);
                }
                case -1: {
                    return findDBObjectByIdInSublist(id, a, middle);
                }
            }
        }
        return null;
    }

}
